/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "glheaders.h"

#include <assert.h>
#include <string.h>
#include <math.h>
#include "renderUtils.h"
#include "mouseUtils.h"
#include "graph.h"
#include "ui/uiUtils.h"
#include "controller.h"
#include "view.h"
#include <sstream>

class LegendDiscrete;
class ColorBox: public Clickable {
public:
	string property;
	string value;
	LegendDiscrete *editor;
	int idx;
	
	ColorBox(int index, string property, string value, LegendDiscrete *editor);
	virtual void draw();	
	//virtual void drag(float , float , float dx, float dy, int ) {}
	virtual void click(float x , float y, int modifiers);
};

ColorBox::ColorBox(int index, string property, string value, LegendDiscrete *editor) {
	this->idx = index;
	this->value = value;
	this->editor = editor;
	this->property = property;
}


class LegendDiscrete: public View {
	string colormap;
	ColorMap *tf;
	bool inited;
	
public:
	LegendDiscrete(const char *title, string type="node") : View("Discrete Legend", title) {
		setPropertyStringList("Data.Colormap", Controller::addVirtualPrefix(type+".colormap"), "colormap");
		setPropertyStringList("Data.Property", Controller::addVirtualPrefix(type+".color"), type);
		setPropertyInt("Data.Columns", 1);
		setMargins(20,20,20,20);
		tf = 0;
		inited = false;
	}
	
	virtual void update(string /*property*/) {
		updateItems();
	}
	
	virtual void updateItems() {
		viewClickables.clear();
		string property = PROPERTY(getPropertyString("Data.Property"));
		vector<string> values = controller->getStringValues(property);
		int columns = max(1,getPropertyInt("Data.Columns"));
		int rowsPerColumn = (int) round(values.size()/columns);
		int rowCount = 0;
		float curX = 0.0;
		float maxW = 0.0;
		float textW, textH;
		for(unsigned int i=0;i<values.size();i++) {
			int idx = controller->stringToInt(property,values[i]);
			ColorBox *c  = new ColorBox(idx, property, values[i], this);
			c->w = 0.1;
			c->h = 0.5/(float) values.size(); 
			c->y = (1.0 - (float) (rowCount+1)/(float) rowsPerColumn);
			c->x = curX;
			getTextBounds(textW, textH, (char*) values[i].c_str());
			maxW = fmax(textW, maxW);
			rowCount++;
			if(rowCount>=rowsPerColumn) {
				rowCount = 0;
				curX += 0.1 + 2*xpixels(5.0) + maxW;
				maxW = 0.0;
			}
			//print("Adding view clickable %s %f %d [%f %f]\n", values[i].c_str(), curX, rowCount, c->x, c->y);
			addViewClickable(c);
		}
	}
	
	void updateColor(int index, float rgb[3]) {
		if(tf) {
			tf->setColor(index, rgb[0], rgb[1], rgb[2]);
			tf->generate();
		}
	}
	
	void render() {
		View::render();
		if(!inited) {
			updateItems();
			inited = true;
		}
		
		colormap = PROPERTY(getPropertyString("Data.Colormap"));
		tf = controller->getColormap(colormap);
	}
	string getColormap() {
		return colormap;
	}
};

void ColorBox::click(float /*x*/ , float /*y*/, int /*modifiers*/) {
	float rgb[3];
    bool valid = queryColor(rgb); 
    if(valid) {
		editor->updateColor(idx, rgb);
    }
}


void ColorBox::draw() {
	float rgb[3];
	string colormap = editor->getColormap();
	ColorMap *cmap = editor->controller->getColormap(colormap);
	if(cmap && cmap->getType()==ColorMap::CONTINUOUS) {
		int index = editor->controller->stringToInt(property, value);
		int n = editor->controller->numStringValues(property);
		float f = (n>0)? (0.0 + (float) index)/(float) (n-1): 0;
		editor->controller->getColor(colormap, f, rgb);
	} else {
		int index = editor->controller->stringToInt(property, value);
		editor->controller->getColor(colormap, index, rgb);
		//int i = getAsInteger(bag, property, 0.0);
		//getColor(colormap, i, rgb);
	}
	float width = w;
	float dy = h;
	glColor4f(rgb[0], rgb[1], rgb[2], 1.0);
	glBegin(GL_QUADS);
	{
		glVertex2f(x    , y);
		glVertex2f(x+width, y);
		glVertex2f(x+width, y+dy);
		glVertex2f(x    , y+dy);
	}
	glEnd();
	
	glColor4f(0,0,0,0.5);
	glBegin(GL_LINE_STRIP);
	{
		//glColor4f(1,1,1, 1.0);
		glVertex2f(x    , y);
		glVertex2f(x+width, y);
		glVertex2f(x+width, y+dy);
	}
	glEnd();
	glBegin(GL_LINE_STRIP);
	{
		//glColor4f(0,0,0, 1.0);
		glVertex2f(x+width, y+dy);
		glVertex2f(x    , y+dy);
		glVertex2f(x    , y);
	}
	glEnd();
	
	
	glBegin(GL_QUADS);
	{
		glColor4f(0.5,0.5,0.5, 0.0);
		glVertex2f(x+width, y);
		glVertex2f(x+width, y+dy);
		glColor4f(0.0,0.0,0.0, 0.1);
		glVertex2f(x    , y+dy);
		glVertex2f(x    , y);
	}
	glEnd();
	
	glColor4f(0,0,0, 1.0);
//	editor->renderText(x + w + editor->xpixels(5.0), y, (char* )value.c_str(), "left", "bottom");
	drawText(x + w + editor->xpixels(5.0), y, (char* )value.c_str());
}



/************************************
 VIEW CREATORS 
 **************************************/

View *createDiscreteLegendNode(string name) {
	LegendDiscrete *legend3 = new LegendDiscrete(name.c_str(), "node");
	legend3->setLocation(0.6, -0.55);
	legend3->setSize(0.5, 0.3);
	legend3->setVisible(true);
	legend3->addCloseButton();
	legend3->addTransparentButton();
	return legend3;
}
View *createDiscreteLegendEdge(string name) {
	LegendDiscrete *legend3 = new LegendDiscrete(name.c_str(), "edge");
	legend3->setLocation(0.6, -0.55);
	legend3->setSize(0.5, 0.3);
	legend3->setVisible(true);
	legend3->addCloseButton();
	legend3->addTransparentButton();
	return legend3;
}


/**************************************************************************
  0705 1600 - discrete legend of all properties of NODEs.
  ************************************************************************/

class NodeLegendDiscrete: public LegendDiscrete{
public:
    string strLegendType;
    string strViewType;
    int iViewNum;

    void setViewTypeNum(const char *title){
        strViewType = "";
        strViewType = title;
        char delims[] = "_";
        char *str = (char*) strViewType.c_str();
        char *tok = strtok(str, delims);
        strViewType = string(tok);
        // "Discrete Legend Node" or "Discrete Legend Edge"

        iViewNum = 1;
        tok = strtok(NULL, delims);
        iViewNum = atoi(tok); // "1" or more...

    }

    NodeLegendDiscrete(const char *title): LegendDiscrete(title){
        setViewTypeNum(title);
        setPropertyStringList("Colormap", "discrete.colormap", "colormap");
    }
    virtual ~NodeLegendDiscrete(){}

    void setType(string strType){
        this->strLegendType = strType;
    }

    vector<string> filterOutStringProperties(vector<string> vecInputProps){
        vector<string> vecReturn;

        for(int i=0; i<vecInputProps.size(); i++){
            string strPropName = vecInputProps[i];
            int iTypeProp = this->controller->getPropertyType(strPropName);

            if(iTypeProp==PROPERTY_INT ||
                    iTypeProp==PROPERTY_FLOAT ||
                    iTypeProp==PROPERTY_SIGNED
                    ){
                vecReturn.push_back(strPropName);
            }

        }
        return vecReturn;

    }


    virtual void updateItems() {
        viewClickables.clear();

        if(strLegendType!= "node" && strLegendType!="edge"){
            return;
        }
        vector<string> nodeProps = this->controller->getRegisteredProperties(strLegendType);

        // check if all propertes are not STRING prop.


        vector<string> values = filterOutStringProperties(nodeProps);  //controller->getStringValues(property);



        string property = strLegendType; //PROPERTY(getPropertyString("Property"));

        //print("Getting values for property %s\n", property.c_str());
        int columns = max(1,getPropertyInt("Data.Columns"));
        int rowsPerColumn = (int) round(values.size()/columns);
        int rowCount = 0;
        float curX = 0.0;
        float maxW = 0.0;
        float textW, textH;

        for(unsigned int i=0;i<values.size();i++) {
                int idx = controller->stringToInt(property,values[i]);
                ColorBox *c  = new ColorBox(idx, property, values[i], this);
                c->w = 0.1;
                c->h = 0.5/(float) values.size();
                c->y = (1.0 - (float) (rowCount+1)/(float) rowsPerColumn);
                c->x = curX;
                getTextBounds(textW, textH, (char*) values[i].c_str());
                maxW = fmax(textW, maxW);
                rowCount++;
                if(rowCount>=rowsPerColumn) {
                        rowCount = 0;
                        curX += 0.1 + 2*xpixels(5.0) + maxW;
                        maxW = 0.0;
                }
                //print("Adding view clickable %s %f %d [%f %f]\n", values[i].c_str(), curX, rowCount, c->x, c->y);
                addViewClickable(c);
        }
    }

};
View *createNodeDiscreteLegend(string name) {
        NodeLegendDiscrete *legend3 = new NodeLegendDiscrete(name.c_str());
        legend3->setType("node");

        legend3->setLocation(0.6, -0.55);
        legend3->setSize(0.5, 0.3);
        legend3->setVisible(true);
        legend3->addCloseButton();
        legend3->addTransparentButton();
        return legend3;
}


class EdgeLegendDiscrete: public NodeLegendDiscrete{
public:

    EdgeLegendDiscrete(const char *title): NodeLegendDiscrete(title){
    }
    virtual ~EdgeLegendDiscrete(){ }
};

View *createEdgeDiscreteLegend(string name) {
        EdgeLegendDiscrete *legend3 = new EdgeLegendDiscrete(name.c_str());
        legend3->setType("edge");

        legend3->setLocation(0.6, -0.55);
        legend3->setSize(0.5, 0.3);
        legend3->setVisible(true);
        legend3->addCloseButton();
        legend3->addTransparentButton();
        return legend3;
}

